#include "ReflectableForwardLinkedList.h"

#include "Reflection/Type.h"

CReflectableForwardLinkedListNode::CReflectableForwardLinkedListNode(CType *InType)
{
    Data = malloc(InType->GetSize());
    InType->InitInstance(Data);
}

CReflectableForwardLinkedListNode::CReflectableForwardLinkedListNode(CType *InType, void *InData)
{
    Data = malloc(InType->GetSize());
    InType->CloneInstance(InData,Data);
}

CReflectableForwardLinkedListNode::CReflectableForwardLinkedListNode(CType *InType, const CReflectableForwardLinkedListNode &Other)
{
    Data = malloc(InType->GetSize());
    InType->CloneInstance(Other.Data,Data);

    if(Other.Next)
    {
        Next = new CReflectableForwardLinkedListNode(InType,*Other.Next);
    }

}

void *CReflectableForwardLinkedListNode::GetData() const
{
    return Data;
}

void CReflectableForwardLinkedListNode::Destroy(CType *InType)
{
    if(Next)
    {
        Next->Destroy(InType);
    }

    if (Data)
    {
        InType->EraseInstance(Data);
        free(Data);
        Data = nullptr;
    }
}

CReflectableForwardLinkedListNode::~CReflectableForwardLinkedListNode()
{
    assert(Data == nullptr && "should invoke Destroy() before delete node");
}

CReflectableForwardLinkedList::CReflectableForwardLinkedList(CType *InType, const CReflectableForwardLinkedList &Other)
{
    if(Other.Head)
    {
        Head = new CReflectableForwardLinkedListNode(InType,*Other.Head);
    }

    Size = Other.Size;
}

void CReflectableForwardLinkedList::Clear(CType *InType)
{
    if(Head)
    {
        Head->Destroy(InType);
        delete Head;
        Head = nullptr;
    }

    Size = 0;
}

void CReflectableForwardLinkedList::Destroy(CType *InType)
{
    if(Head)
    {
        Head->Destroy(InType);
        delete Head;
        Head = nullptr;
    }

}

CReflectableForwardLinkedList::~CReflectableForwardLinkedList()
{
    assert(Head == nullptr && "should invoke Destroy() before delete list");
}

bool CReflectableForwardLinkedList::Empty() const
{
    return Head == nullptr;
}

void CReflectableForwardLinkedList::InsertAtHead(CReflectableForwardLinkedListNode *Node)
{
    Node->Next = Head;
    Head = Node;
    Size++;
}

CReflectableForwardLinkedListNode *CReflectableForwardLinkedList::PopNode()
{
    if(Head == nullptr)
    {
        return nullptr;
    }

    auto Result = Head;
    Head = Head->Next;
    Result->Next = nullptr;
    Size--;
    return Result;
}

void CReflectableForwardLinkedList::RemoveNextNode(CType* InType,CReflectableForwardLinkedListNode *Node)
{
    if(Node->Next == nullptr)
    {
        return;
    }

    auto Next = Node->Next;
    Node->Next = Next->Next;
    Next->Next = nullptr;
    Size--;
    Next->Destroy(InType);
    delete Next;      
}

void CReflectableForwardLinkedList::RemoveHead(CType *InType)
{
    if(Head == nullptr)
    {
        return;
    }

    auto Next = Head->Next;
    Head->Next = nullptr;
    Head->Destroy(InType);
    delete Head;
    Head = Next;
    Size--;
}

CReflectableForwardLinkedListIterator CReflectableForwardLinkedList::begin() const
{
    return CReflectableForwardLinkedListIterator(Head);
}

CReflectableForwardLinkedListIterator CReflectableForwardLinkedList::end() const
{
    return CReflectableForwardLinkedListIterator(nullptr);
}

uint64_t CReflectableForwardLinkedList::GetHashCode(CType *InType) const
{
    uint64_t Result = 0;
    for(auto& Node : *this)
    {
        Result += InType->GetInstanceHash(Node.GetData());
    }
    return Result;
}

bool CReflectableForwardLinkedList::IsEqual(CType *InType, const CReflectableForwardLinkedList &Other) const
{
    if(Size != Other.Size)
    {
        return false;
    }

    auto ThisIterator = begin();
    auto OtherIterator = Other.begin();

    while(true)
    {
        if(ThisIterator == end())
        {
            if(OtherIterator != Other.end())
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        if(OtherIterator == Other.end())
        {
            if(ThisIterator != end())
            {
                return false;
            }
            else
            {
                return true;
            }
            break;
        }

        if(!InType->IsInstanceEqual(ThisIterator->GetData(),OtherIterator->GetData()))
        {
            return false;
        }

        ++ThisIterator;
        ++OtherIterator;
    }


    assert(false && "unreachable");
    return false;
}


CReflectableForwardLinkedListIterator::CReflectableForwardLinkedListIterator(CReflectableForwardLinkedListNode *InNode)
{
    Node= InNode;
}

bool CReflectableForwardLinkedListIterator::operator==(const CReflectableForwardLinkedListIterator &Other)
{
    return Node == Other.Node;
}

bool CReflectableForwardLinkedListIterator::operator!=(const CReflectableForwardLinkedListIterator &Other)
{
    return Node != Other.Node;
}

CReflectableForwardLinkedListIterator &CReflectableForwardLinkedListIterator::operator++()
{
    if(Node)
    {
        Node = Node->Next;
    }
    return *this;
}

CReflectableForwardLinkedListNode &CReflectableForwardLinkedListIterator::operator*()
{
    return *Node;
}

CReflectableForwardLinkedListNode *CReflectableForwardLinkedListIterator::operator->()
{
    return Node;
}
